#include "Modelo.h"
#include <string>
#include <iostream>
#include "PPMC.h"
#include <math.h>

using namespace std;

void FrecuenciasSimbolos::addSimbolos(const FrecuenciasSimbolos &frecuenciasSimbolos,bool agregarESC)
{
    FrecuenciasSimbolos::const_iterator itFreqSim=frecuenciasSimbolos.begin();
    
    // Se recorren las frecuencias de símbolos pasados
    while (itFreqSim!=frecuenciasSimbolos.end())
    {
    	if ((itFreqSim->first!=SimboloESC) || agregarESC)
    	{
			// se busca el símbolo iterado
			FrecuenciasSimbolos::iterator itFreqSimThis=
				this->find(itFreqSim->first);

			// En caso de no haber encontrado el símbolo...
			if (itFreqSimThis==this->end())
				// ...se agrega a this.
				(*this)[itFreqSim->first]=itFreqSim->second;
    	}

        itFreqSim++;
    }
}

Modelo::Modelo(bool agregarEsc)
{
    // Se agrega el símbolo Esc al modelo
    if (agregarEsc)
        this->addSimbolo(SimboloESC);
}

Modelo::~Modelo()
{
    DatosSimbolosModelo::iterator itDatoSimbolo=
        this->datosSimbolos.begin();
    
    while (itDatoSimbolo!=this->datosSimbolos.end())
    {
        if (itDatoSimbolo->second.modeloSiguienteOrden!=NULL)
            // Se llama al destructor en forma recursiva
            delete itDatoSimbolo->second.modeloSiguienteOrden;

        itDatoSimbolo++;
    }
}

/**
* @return true si el simbolo se encuentra dentro del modelo, sino false.
*/
bool Modelo::contiene(const Simbolo simbolo)
{
    return (this->datosSimbolos.find(simbolo)!=this->datosSimbolos.end());
}

/**
 * @return Una instancia de la clase que implementa este método.
 */
Modelo *Modelo::crearInstancia()
{
	return new Modelo();
}

/**
* @pre: El símbolo no debe existir en el modelo.
* Agrega el simbolo al modelo con frecuencia 1 y modelo de siguiente orden 
* NULL.
*/
void Modelo::addSimbolo(const Simbolo simbolo)
{
    DatoSimboloModelo &dato=this->datosSimbolos[simbolo];
    dato.frecuencia=1;
    dato.modeloSiguienteOrden=NULL;
}

/**
* Se actualiza la frecuencia del símbolo o se agrega el simbolo en las frecuencias
* de simbolos del modelo (también actualiza el ESC en caso de agregar).
* @param simbolo Simbolo a ser agregado o quitado.
*/
void Modelo::actualizarFrecuencia(const Simbolo simbolo)
{
    DatosSimbolosModelo::iterator itDatoSimbolo=this->datosSimbolos.find(simbolo);
    
    // En caso de no existir el símbolo en el modelo...
    if (itDatoSimbolo==this->datosSimbolos.end())
    {
        // ...se agrega
        this->addSimbolo(simbolo);
        
        // ...se actualiza la frecuencia del ESC
        this->datosSimbolos[SimboloESC].frecuencia=
            this->datosSimbolos.size()-1;
    }
    else
    {
        // Se actualiza la frecuencia del símbolo
        itDatoSimbolo->second.frecuencia++;
    }
}

/**
* Quita el simbolo del modelo.
* @param simbolo Simbolo a ser quitado.
*/
void Modelo::removeSimbolo(const Simbolo simbolo)
{
    this->datosSimbolos.erase(simbolo);
}

/**
* @return Las frecuencias de los simbolos que se encuentran en el modelo.
*/
const FrecuenciasSimbolos &Modelo::getFrecuenciasSimbolos()
{
    return (FrecuenciasSimbolos &)this->datosSimbolos;
}

/**
* Obtiene para el símbolo pasado el modelo de siguiente orden.
* @para simbolo Simbolo a buscar para el cual se obtendrá el modelo de 
* siguiente orden.
* @return El modelo de siguiente orden. NULL en caso que no exista modelo 
* para el símbolo pasado.
*/
Modelo *Modelo::getModeloSiguienteOrden(const Simbolo simbolo)
{
    DatosSimbolosModelo::iterator itDatoSimbolo=
        this->datosSimbolos.find(simbolo);
    
    // En caso de no existir el símbolo en el modelo...
    if (itDatoSimbolo==this->datosSimbolos.end())
        return NULL;
    
    return itDatoSimbolo->second.modeloSiguienteOrden;
}

/**
* @pre Debe existir el simbolo en el modelo.
* Se crea un nuevo modelo de un orden mayor para el simbolo a insertar al 
* final del contexto en caso que el mismo no exista, sino no se hace nada.
* @param simbolo Simbolo para el cual se creará el modelo en caso que no 
* exista.
*/
Modelo *Modelo::crearModeloSiguienteOrdenSiNoExiste(const Simbolo simbolo)
{
    // Se obtienen los datos del simbolo

	DatosSimbolosModelo::iterator itdsm=
		this->datosSimbolos.find(simbolo);

	if (itdsm==this->datosSimbolos.end())
	{
		throw std::string("El simbolo pasado no existe en el modelo");
	}

    // En caso que el simbolo no tenga asociado un modelo para el siguiente 
    // orden...
    if (itdsm->second.modeloSiguienteOrden==NULL)
        // ...se crea el modelo.
    	itdsm->second.modeloSiguienteOrden=this->crearInstancia();
    
    return itdsm->second.modeloSiguienteOrden;
}

/**
 * @pre El simbolo debe existir.
 * @param simbolo Simbolo para el cual obtendra la frecuencia.
 * @return La frecuencia del simbolo.
 */
unsigned long Modelo::getFrecuenciaSimbolo(const Simbolo simbolo)
{
	DatosSimbolosModelo::iterator itdsm=
		this->datosSimbolos.find(simbolo);

	if (itdsm==this->datosSimbolos.end())
	{
		throw std::string("El simbolo pasado no existe en el modelo");
	}

	return itdsm->second.frecuencia;
}


ModeloMenosUno::ModeloMenosUno():
	Modelo(false)
{
	for (int i=0;i<=255;i++)
	{
		char c=i;
		Simbolo simbolo=c;
		this->addSimbolo(simbolo);
	}

	this->addSimbolo(SimboloEOF);
}

ModeloMenosUno::~ModeloMenosUno()
{
}

/**
* @pre: El símbolo no debe existir en el modelo.
* Agrega el simbolo al modelo con frecuencia 1 y modelo de siguiente orden
* NULL.
*/
void ModeloInicializaEsc::addSimbolo(const Simbolo simbolo)
{
    DatoSimboloModelo &dato=this->datosSimbolos[simbolo];
    dato.frecuencia=3;
    dato.modeloSiguienteOrden=NULL;
}

/**
 * @return Una instancia de la clase que implementa este método.
 */
Modelo *ModeloInicializaEsc::crearInstancia()
{
	return new ModeloInicializaEsc();
}

ModeloInicializaEsc::ModeloInicializaEsc():Modelo(false)
{
	Modelo::addSimbolo(SimboloESC);
}

ModeloInicializaEsc::~ModeloInicializaEsc()
{
}

/**
* Se actualiza la frecuencia del símbolo o se agrega el simbolo en las frecuencias
* de simbolos del modelo (también actualiza el ESC en caso de agregar).
* @param simbolo Simbolo a ser agregado o quitado.
*/
void ModeloInicializaEsc::actualizarFrecuencia(const Simbolo simbolo)
{
    DatosSimbolosModelo::iterator itDatoSimbolo=this->datosSimbolos.find(simbolo);

    // En caso de no existir el símbolo en el modelo...
    if (itDatoSimbolo==this->datosSimbolos.end())
    {
        // ...se agrega
        this->addSimbolo(simbolo);

        // ...se actualiza la frecuencia del ESC
        this->datosSimbolos[SimboloESC].frecuencia=
            1+(this->datosSimbolos.size()-2)*3;
    }
    else
    {
        // Se actualiza la frecuencia del símbolo
        itDatoSimbolo->second.frecuencia=itDatoSimbolo->second.frecuencia+3;
    }
}
